If you want to remove unnecessary headers from a Web App in Azure App Services, here are some things we need to know.

App Services run on a PaaS. This means that not everything can be touched or changed. We will be addressing the Azure App Service (Windows).

In particular:

  • 502, 503 and 403 http statuses are returned from the front-end instances and never reach the worker, which means that we cannot influence the headers there

Removing the X-AspNet-Version header

You can do this by editing the web.config file in KUDU. If you don't have one, just create a web.config file in the wwwroot dir.

This is between the <configuration> </configuration>

<system.web>
    <httpRuntime enableVersionHeader="false" /> <!-- Removes ASP.NET version header. Not needed for Ghost running in iisnode -->
</system.web>

Removing the X-Powered-By: ASP.NET header

Again under <configuration>

<system.webServer>
    <httpProtocol>
      <customHeaders>
        <remove name="X-Powered-By" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>

Removing the Server header

Again under <configuration>

<system.webServer>
   <security>
      <requestFiltering removeServerHeader="true" />
      <!-- Removes Server header in IIS10 or later and also in Azure Web Apps -->
   </security>
</system.webServer>

These do not remove the Server header from 500.0 errors. You can't really do that but what you can do is rename it. More info here - https://stackoverflow.com/questions/50150160/remove-server-header-on-response-on-500-http-error

The web.config so far

So if you want to do all 3 in one, here is how your web.config should look like

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
   <system.web>
      <httpRuntime enableVersionHeader="false" />
      <!-- Removes ASP.NET version header.  -->
   </system.web>
   <system.webServer>
      <security>
         <requestFiltering removeServerHeader="true" />
         <!-- Removes Server header in IIS10 or later and also in Azure Web Apps -->
      </security>
      <httpProtocol>
         <customHeaders>
            <clear />
            <!-- Gets rid of the other unwanted headers -->
            <remove name="X-Powered-By" />
         </customHeaders>
         <redirectHeaders>
            <clear />
         </redirectHeaders>
      </httpProtocol>
   </system.webServer>
</configuration>

Removing the X-Powered-By: PHP/7.2.19 (or any other version) header

Now this header exists only in PHP applications and is controlled by PHP itself. What you usually need to do is go to your PHP folder and open php.ini and find this line:

expose_php = On (change it to Off to remove the header)

In Azure App Services unfortunately is a little different. The usual way of influencing php settings by placing a .user.ini file in wwwroot will not work as the expose_php setting is a Core setting and will not be affected. So the way to do it is this:

  1. Go to your Web App's Configuration blade
  2. Under Application Settings click New application setting
  3. Name: PHP_INI_SCAN_DIR
  4. Value: D:\home\site
  5. Click OK and Save

Azure Web App second php.ini

What we just did is to show PHP that we will be scanning this directoroy for additional .ini files. Now let's go and create a newphp.ini file in the site dir of your Web App. In it place expose_php = Off in a single line. Save . Restart the Web App. The X-Powered-By: PHP/7.2.19 header should be gone. You can also check if the settings have worked by using a ()phpinfo page and search for expose_php to check if the new values are Off.

Adding headers for improved security

So we were stripping down headers until now but now it's time to add some for improved security. If you run your website through a tool like https://securityheaders.com/ you will see what is it that your website is missing from the best practices. 

Here is a great blog posts that explains it all. https://scotthelme.co.uk/hardening-your-http-response-headers/https://infosec.mozilla.org/guidelines/web_security

Here is a sample web.config that will allow these headers. It all fits between <configuration> -> <system.webServer>

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
   <system.web>
      <httpRuntime enableVersionHeader="false" />
      <!-- Removes ASP.NET version header.  -->
   </system.web>
   <system.webServer>
      <security>
         <requestFiltering removeServerHeader="true" />
         <!-- Removes Server header in IIS10 or later and also in Azure Web Apps -->
      </security>
      <httpProtocol>
         <customHeaders>
            <clear />
            <!-- Gets rid of the other unwanted headers -->
            <remove name="X-Powered-By" />
         </customHeaders>
         <redirectHeaders>
            <clear />
         </redirectHeaders>
      </httpProtocol>
   </system.webServer>
</configuration>

The Complete web.config

So Removing the unnecessary headers and adding the additional ones for security. Here is how it all looks like in a single web.config. After you implement it you should get A+ on https://securityheaders.com/. Try other security portals and see how your app rates - https://www.ssllabs.com/ssltesthttps://observatory.mozilla.org/.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
   <system.web>
      <httpRuntime enableVersionHeader="false" />
      <!-- Removes ASP.NET version header. -->
   </system.web>
   <system.webServer>
      <security>
         <requestFiltering removeServerHeader="true" />
         <!-- Removes Server header in IIS10 or later and also in Azure Web Apps -->
      </security>
      <httpProtocol>
         <customHeaders>
            <clear />
            <!-- Gets rid of the other unwanted headers -->
            <add name="X-Frame-Options" value="SAMEORIGIN"/>
            <!-- disables iframing the website from other than the origin -->
            <add name="X-Xss-Protection" value="1; mode=block"/>
            <!-- configure the built in reflective XSS protection found in Internet Explorer, Chrome and Safari (Webkit). -->
            <add name="X-Content-Type-Options" value="nosniff"/>
            <!-- prevents Google Chrome and Internet Explorer from trying to mime-sniff the content-type of a response away from the one being declared by the server -->
            <add name="Content-Security-Policy" value="upgrade-insecure-requests; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; object-src 'none';"/>
            <!-- It is a compplex policy of what can execute. This can be really restrictive so please read up on it if you tighten it. I have laid out basic setting that is very permissive and will not save you from XSS attacks. https://content-security-policy.com/ or https://infosec.mozilla.org/guidelines/web_security#content-security-policy -->
            <add name="Referrer-Policy" value="strict-origin-when-cross-origin"/>
            <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy - Sets the referrer policy. Use for CORS -->
            <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload"/>
            <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security - HSTS allows you to tell a browser that you always want a user to connect using HTTPS instead of HTTP. his policy will enforce TLS on your site and all subdomains for a year. -->
            <add name="Permissions-Policy" value="accelerometer=(self), camera=(self), geolocation=(self), gyroscope=(self), magnetometer=(self), microphone=(self), payment=(self), usb=(self)" />
            <!-- used to disable certain features https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy -->
            <add name="X-Permitted-Cross-Domain-Policies" value="none"/>
            <!-- The X-Permitted-Cross-Domain-Policies header tells clients like Flash and Acrobat what cross-domain policies they can use. If you don’t want them to load data from your domain, set the header’s value to none -->
            <add name="Expect-CT" value="max-age=86400, enforce"/>
            <!-- The Expect-CT header allows sites to opt in to reporting and/or enforcement of Certificate Transparency requirements, which prevents the use of misissued certificates for that site from going unnoticed. -->
            <remove name="X-Powered-By" />
            <!-- removes the X-Powered-By:ASP.NET header -->
         </customHeaders>
         <redirectHeaders>
            <clear />
         </redirectHeaders>
      </httpProtocol>
   </system.webServer>
</configuration>

Here is an example of a restrictive CSP policy. No inline CSS or JS allowed. Images, media, form, script and style tags only from the whitelisted in the CSP domains. It also shows the usage of nonce for <script> tags that cannot go into a .js file.

<add name="Content-Security-Policy" value="
upgrade-insecure-requests; 
default-src 'none'; 
style-src 'self' https://apis.google.com https://code.jquery.com https://script.hotjar.com https://connect.facebook.net/ https://platform.twitter.com; 
script-src 'self' https://www.google-analytics.com https://ssl.google-analytics.com https://ajax.googleapis.com https://apis.google.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://code.jquery.com https://cdnjs.cloudflare.com https://connect.facebook.net https://static.hotjar.com https://script.hotjar.com https://platform.twitter.com https://www.statcounter.com https://c.statcounter.com https://www.googletagmanager.com 'nonce-g00gl3T4gM4n4g3r' 'nonce-tw1tt3r' 'nonce-s747C0un73r' 'nonce-f4c3b00k' 'nonce-r3cap7ch4' 'nonce-4p1sG00gl3pL47f0rM' 'nonce-4p1sG00gl3pL47f0rM2' 'nonce-l0g1nF0rM' 'nonce-l0g0u7' 'nonce-cH4ng3PwD'; 
frame-src 'self' https://accounts.google.com/ https://www.google.com/recaptcha/ https://www.youtube.com https://web.facebook.com/ https://www.facebook.com/ https://vars.hotjar.com/ https://platform.twitter.com/ https://clips.twitch.tv/ https://player.twitch.tv/; 
frame-ancestors 'self'; 
form-action 'self'; 
object-src 'none'; 
img-src 'self' https://www.facebook.com https://syndication.twitter.com/ https://script.hotjar.com http://script.hotjar.com www.googletagmanager.com https://www.google-analytics.com; 
base-uri 'self'; 
child-src 'self' *.google.com *.twitter.com *.hotjar.com www.facebook.com https://www.youtube.com https://player.twitch.tv; 
media-src 'self' https://djongov.azureedge.net; 
font-src 'self' *.hotjar.com; 
connect-src 'self' https://www.googletagmanager.com https://c.statcounter.com https://*.hotjar.com wss://*.hotjar.com https://vc.hotjar.io:* https://www.google-analytics.com https://c.statcounter.com; 
worker-src 'self';" />

Linux Apache

If you are running a Linux Apache web app. This is the equivalent. Put this into your .htaccess file

#Adding security headers
<IfModule mod_headers.c>
Header add X-Frame-Options "SAMEORIGIN"
Header add X-Xss-Protection "1; mode=block"
Header add Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header add X-Content-Type-Options "nosniff"
Header add Referrer-Policy "strict-origin-when-cross-origin"
Header add Permissions-Policy "accelerometer=(self), camera=(self), geolocation=(self), gyroscope=(self), magnetometer=(self), microphone=(self), payment=(self), usb=(self)"
Header add X-Permitted-Cross-Domain-Policies "none"
Header add Expect-CT "max-age=86400, enforce"
Header add Content-Security-Policy "block-all-mixed-content; frame-ancestors 'self'; form-action 'self'; object-src 'none'; base-uri 'self';"
</IfModule>

References used

http://blog.paulbouwer.com/2013/01/09/asafaweb-excessive-headers-and-windows-azure/

https://stackoverflow.com/questions/50150160/remove-server-header-on-response-on-500-http-error

https://blogs.msdn.microsoft.com/hosamshobak/2015/03/20/azure-websites-how-to-change-php-ini-settings/

https://scotthelme.co.uk/hardening-your-http-response-headers/

https://infosec.mozilla.org/guidelines/web_security